package net

import (
	"fmt"
	"net/http"

	"github.com/gogf/gf/v2/frame/g"
	"github.com/gogf/gf/v2/net/ghttp"
	"github.com/gogf/gf/v2/util/gutil"

	"github.com/google/uuid"
)

const (
	headerConnection   = "Connection"
	headerContentType  = "Content-Type"
	headerCacheControl = "Cache-Control"
)

func NewResponse(req *ghttp.Request) *response {
	return &response{
		req: req,
	}
}

type response struct {
	req *ghttp.Request
}

func (r *response) SetHeader(key, value string) *response {
	r.req.Response.Header().Set(key, value)

	return r
}
func (r *response) setJsonHeader() {
	r.req.Response.Header().Set(headerContentType, httpHeaderContentTypeJson)
}
func (r *response) setXmlHeader() {
	r.req.Response.Header().Set(headerContentType, httpHeaderContentTypeXml)
}
func (r *response) setHtmlHeader() {
	r.req.Response.Header().Set(headerContentType, "text/html; charset=utf-8")
}
func (r *response) setImageHeader() {
	r.req.Response.Header().Set(headerContentType, "image/*")
}
func (r *response) setCacheHeader() { // todo
	r.req.Response.Header().Set(headerContentType, "text/html; charset=utf-8")
}

func (r *response) checkCacheControlHeader() {
	if r.req.Response.Header().Get(headerCacheControl) == "" {
		r.SetCacheAge(0)
	}
}
func (r *response) SetCacheAge(seconds int) *response {
	value := "private,no-cache"
	if seconds > 0 {
		value = fmt.Sprintf("public,max-age=%d", seconds)
	}

	r.req.Response.Header().Set(headerCacheControl, value)

	return r
}
func (r *response) WithRequestId() *response {
	requestId := uuid.New().String()

	r.SetHeader("W-Request-Id", requestId)

	return r
}
func (r *response) SetSse() *response {
	r.req.Response.Header().Set(headerConnection, "keep-alive")
	r.req.Response.Header().Set(headerContentType, "text/event-stream")
	r.req.Response.Header().Set(headerCacheControl, "no-cache")

	return r
}

func (r *response) WriteJson(data *Json) {
	if gutil.IsEmpty(data.Status) {
		data.Status = 200
	}

	if data.Status == 200 && data.Message == "" {
		data.Message = "success"
	}
	if data.Status == 401 && data.Message == "" {
		data.Message = "unauthorized"
	}

	if r.req.Method == http.MethodPost && data.Status != 200 {
		_ = r.req.ParseMultipartForm(0)
	}

	r.setJsonHeader()
	r.checkCacheControlHeader()

	r.req.Response.WriteStatusExit(data.Status, data)
}
func (r *response) WriteHtml(data interface{}) {
	r.setHtmlHeader()
	r.checkCacheControlHeader()

	r.req.Response.WriteExit(data)
}
func (r *response) WriteBinary(status int, content interface{}) {
	r.req.Response.WriteStatusExit(status, content)
}

func (r *response) ResponseOk(data ...interface{}) { // 200 Ok
	r.WriteJson(NewJson(http.StatusOK, "success", data...))
}
func (r *response) ResponseOkWithTaskId(taskId string) {
	r.ResponseOk(g.Map{
		"task_id": taskId,
	})
}
func (r *response) ResponseOKWithError(status int, message string) {
	r.req.Response.WriteStatusExit(http.StatusOK, NewJson(status, message))
}

func (r *response) ResponseBadRequest(message string) { // 400 BadRequest
	r.WriteJson(NewJson(http.StatusBadRequest, message))
}
func (r *response) ResponseUnauthorized(message string) { // 401 Unauthorized
	r.WriteJson(NewJson(http.StatusUnauthorized, message))
}
func (r *response) ResponseNotFound(message string) { // 404 Not Found
	r.WriteJson(NewJson(http.StatusUnauthorized, message))
}
func (r *response) ResponseRequestTooLarge() { // 413 Request body too large
	r.WriteJson(NewJson(http.StatusRequestEntityTooLarge, http.StatusText(http.StatusRequestEntityTooLarge)))
}
func (r *response) ResponseTooManyRequest() { // 429 Too many request
	r.WriteJson(NewJson(http.StatusTooManyRequests, http.StatusText(http.StatusTooManyRequests)))
}

func (r *response) ResponseServerError(message string, data ...interface{}) { // 500 ServerError
	r.WriteJson(NewJson(http.StatusInternalServerError, message, data...))
}

func (r *response) RedirectTo(uri string, code ...int) {
	r.req.Response.RedirectTo(uri, code...)
}
func (r *response) DownloadFile(path string, name ...string) {
	r.req.Response.ServeFileDownload(path, name...)
}

func (r *response) ResponseException(e *Exception) {
	r.SetCacheAge(0)

	r.setJsonHeader()

	status := http.StatusOK
	if e.HttpStatus > 0 {
		status = e.HttpStatus
	}

	r.req.Response.WriteStatusExit(status, e)
}

func (r *response) WriteEventMessage(event *Event) {
	event.setEvent(event.Eof)
	message := event.prepareMessage()

	rw := r.req.Response.Writer.RawWriter()
	flusher, ok := rw.(http.Flusher)
	if !ok {
		r.ResponseServerError("Streaming unsupported!", http.StatusInternalServerError)
		return
	}

	_, err := rw.Write(message)
	if err != nil {
		r.ResponseServerError("Raw writer write error", http.StatusInternalServerError)
		return
	}

	flusher.Flush()
	// r.request.Response.Write(message)
	// r.request.Response.Flush()
}
